﻿
using Nx.Logging;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace Nx.Clients.Http
{
    public abstract class WebApiClient :
 IWebApiClient
    {
        protected readonly ILogger _Logger;
        protected readonly ISerializer _Serializer;
        protected readonly string ServiceUrl;

        private bool _IsBusy;
        public bool IsBusy
        {
            get { return _IsBusy; }
            set
            {
                _IsBusy = value;
            }
        }

        public async Task<T> GetAsync<T>()
        {
            var result = await GetAsync<T>(string.Empty);
            return result;
        }

        public async Task<T> GetAsync<T>(string queryString)
        {
            T result = default(T);

            string jsonString = await GetStringAsync(queryString);

            if (jsonString != null)
            {
                result = _Serializer.Deserialize<T>(jsonString);
                _Logger.Debug("Operation returned an entity : {0}", result);
            }

            return result;
        }

        public async Task<IEnumerable<T>> GetCollectionAsync<T>()
        {
            var result = await GetCollectionAsync<T>(string.Empty);
            return result;
        }

        public async Task<IEnumerable<T>> GetCollectionAsync<T>(string queryString)
        {
            IEnumerable<T> result = Enumerable.Empty<T>();

            string @string = await GetStringAsync(queryString);

            if (@string != null)
            {
                result = _Serializer.DeserializeCollection<T>(@string);
                _Logger.Debug("Operation returned {0} entities", result.Count());
            }

            return result;
        }

        protected async Task<string> GetStringAsync(string queryString)
        {
            string result = null;
            IsBusy = true;

            string query = string.IsNullOrEmpty(queryString) ? ServiceUrl : string.Format(CultureInfo.InvariantCulture, "{0}{1}", ServiceUrl, queryString);
            _Logger.Debug("Executing GET {0}", query);

            try
            {
                IRestResponse response = await ExecuteGetAsync(queryString);

                if (IsOperationSuccessful(response))
                {
                    result = response.Content;
                }
            }
            catch (Exception ex)
            {
                _Logger.Error("Error while client was performing a GET operation : {0}", ex.Message);
                result = null;
            }
            finally
            {
                IsBusy = false;
            }

            return result;
        }

        public async Task<bool> TryPostAsync<T>(T dto)
        {
            bool result = true;

            var response = await PostJsonStringAsync(dto);
            if (!IsOperationSuccessful(response))
            {
                result = false;
            }

            return result;
        }

        public async Task<T> PostAsync<T>(T dto) where T : class
        {
            var response = await PostJsonStringAsync(dto);

            if (IsOperationSuccessful(response))
            {
                return _Serializer.Deserialize<T>(response.Content);
            }

            return null;
        }

        protected async Task<IRestResponse> PostJsonStringAsync<T>(T dto)
        {
            IsBusy = true;

            try
            {
                _Logger.Debug("Executing POST {0}", ServiceUrl);
                string @string = _Serializer.Serialize(dto);
                var response = await ExecutePostAsync(@string);

                return response;
            }
            catch (Exception ex)
            {
                _Logger.Error("Error while client was performing a POST operation : {0}", ex.Message);
            }
            finally
            {
                IsBusy = false;
            }

            return null;
        }

        private bool IsOperationSuccessful(IRestResponse response)
        {
            if (response.StatusCode != HttpStatusCode.OK)
            {
                _Logger.Error("Operation failed : {0}", response.StatusCode);
            }
            else
            {
                _Logger.Debug("Operation successful");
            }

            return response.StatusCode == HttpStatusCode.OK;
        }


        private Task<IRestResponse> ExecuteGetAsync(string queryString)
        {
            TaskCompletionSource<IRestResponse> tcs = new TaskCompletionSource<IRestResponse>();
            RestClient client = new RestClient(ServiceUrl);
            RestRequest request = new RestRequest(queryString, Method.GET);
            client.ExecuteAsync(request, (response) =>
            {
                tcs.SetResult(response);
            });

            return tcs.Task;
        }

        private Task<IRestResponse> ExecutePostAsync(string body)
        {
            TaskCompletionSource<IRestResponse> tcs = new TaskCompletionSource<IRestResponse>();
            RestClient client = new RestClient(ServiceUrl);
            RestRequest request = new RestRequest(Method.POST);

            request.AddParameter("text/json", body, ParameterType.RequestBody);

            client.ExecuteAsync(request, (response) =>
            {
                tcs.SetResult(response);
            });

            return tcs.Task;
        }

        //public async Task Delete(Guid id)
        //{
        //    var result = await _client.DeleteAsync(String.Format("{0}/{1}"
        //                                                        , ServiceUrl, id.ToString()));
        //}

        //public async Task Update(Expense expense)
        //{
        //    var jsonString = Serialize(expense);
        //    var content = new StringContent(jsonString,
        //                                    Encoding.UTF8, "application/json");
        //    var result = await _client.PutAsync(String.Format("{0}/{1}",
        //                                                        ServiceUrl, expense.Id), content);
        //}

        public WebApiClient(ILogFactory logFactory, ISerializer serializer, string serviceUrl)
        {
            _Logger = logFactory.CreateLogger("WebApiClientLogger");
            _Serializer = serializer;
            ServiceUrl = serviceUrl;
        }

        ~WebApiClient()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                _Logger.Dispose();
                _Serializer.Dispose();
            }
        }
    }
}
